/*global confirm, console, alert,  $, jQuery, window, OpenLayers, google, MAIN*/
$( function () {
    /**
     * Main Script for Map Overlay Annotation tool
     */
    /* Global */
    'use strict';

    $.extend({
        generateUUID: function() {
            var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
                var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
                return v.toString(16);
            });
            return uuid;
        }
    });

    // This fetches a JSON object to define shape level metadata.
    // That's easier to configure than editing the javascript file.
    MAIN.formjson = {};
    $( function() {
        $.ajax({
            type: 'GET',
            url: 'data/shape_metadata.json',
            dataType: "json",
            success: function(data) {
                MAIN.formjson = data;
                MAIN.callbacks.buildShapeMetadataDialog(data);
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.error("Couldn't fetch data/shape_metadata.json");
                console.log(jqXHR);
                console.log(textStatus);
                console.log(errorThrown);
            }
        });
    });
    // This section defines the behavior and look of the global dialog boxes
    // Includes login box and annotation metadata box
    MAIN.callbacks.buildLoginDialog = function(jsonData) {
        /**
         * This dialog box is used fjsonDatagin information when the page first
         * loads or is reset.
         */
        MAIN.dialogs.loginDialog = ( function() {
            // construct the login dialog
            var loginDialog = $('<div>').attr({
                id: "login-dialog"
            });

            loginDialog.append($("<form><fieldset><table>"));
            $.each(jsonData, function(key, data) {
                if (key.charAt(0) === "_") {
                    // it's a comment, skip this one.
                    return true;
                }
                if (data.type === 'text') {
                    // append each metadata key/text pair as an input value
                    // on the
                    // form
                    $("table", loginDialog)
                    .append('<tr><td class="right"><label for="' + key + '">' + data.label + '<\/label></td><td><input type="text" name="' + key + '" id="' + key + '" class="text ui-widget-content ui-corner-all" \/></td></tr>');
                } else if (data.type === 'select') {
                    var row = '<tr><td class="right"><label for="' + key + '">' + data.label + '<\/label></td><td><select type="text" name="' + key + '" id="' + key + '" class="text ui-widget-content ui-corner-all">';
                    $.each(data.value, function(i_unused, data) {
                        row = row + '<option>' + data + '<\/option>';
                    });
                    row = row + '<\/select></td></tr>';
                    $("table", loginDialog).append(row);
                } else {
                    throw "unknown metadata type: " + data.type;
                }

            });
            loginDialog.append("Click here to visit the <a href='inseam_explorer'>InSEAM Explorer<\/a> directly.");
            loginDialog.dialog({
                title: "Please Log In",
                autoOpen: false,
                modal: true,
                draggable: false,
                closeOnEscape: false,
                open: function() {
                    // this removes the 'x' close button in the top left corner.
                    $(".ui-dialog-titlebar-close").hide();
                },
                buttons: {
                    "Login": function() {
                        $(this).dialog("close");
                    }
                },
                beforeClose: function() {
                    var invalid = false;
                    $.each(jsonData, function(key,val) {
                        if (key.charAt(0) !== "_") {
                            if ($("#login-dialog #"+key).val() === "") {
                                $("#login-dialog input#"+key).addClass("ui-state-error");
                                invalid = true;
                            } else {
                                $("#login-dialog input#"+key).removeClass("ui-state-error");
                                MAIN.variables.userMetadata[val.label] = $("#login-dialog #"+key).val();
                            }
                        }
                    });
                    if (invalid) {
                        //if any of the fields aren't filled in, don't allow a submit
                        return false;
                    }
                    $.displayUserStatus();
                },
                close: function() {
                }
            });
            return loginDialog;
        }());
    };
    /**
     * This callback is used once the shape metadata has been ajax'd in.
     */
    MAIN.callbacks.buildShapeMetadataDialog = function(jsonData) {
        /**
         * This dialog is used after a shape has been drawn to add metadata to that
         * geometry.
         */
        MAIN.dialogs.shapeMetadataDialog = ( function() {

            // create an empty form and store it at the end of the body
            var form = $('<div><form>').appendTo('body');

            // dynamically build the elements of the form from the formjson object
            $.each(jsonData.elements, function(i_unused, element) {
                var formElement = $('<div>').attr({
                    id: element.id
                }).appendTo(form);
                switch (element.type) {
                    case 'radio':
                        // For the radio button case, loop over each radio button
                        // and define a checkbox and a label
                        $.each(element.values, function(j, value) {
                            var button = $('<input>').attr({
                                type: "radio",
                                name: element.id, // unique id for the group
                                id: element.id, // unique id for the button //TODO:
                                // do we need to append value on id?
                                value: value
                                // value to read when selected
                            });
                            // default the first radio button to be checked
                            if (j === 0) {
                                button.attr("checked", "checked");
                            }
                            formElement.append(button).append($('<label>').attr({
                                "for": element.id
                            }).html(value));
                            formElement.append($('<br\/>'));
                        });
                        break;
                    case 'text':
                        // For the text case, create a label and text input box
                        formElement.append($('<label>').attr({
                            "for": element.id
                        }).html(element.label));
                        formElement.append($('<input>').attr({
                            type: "text",
                            name: element.id,
                            id: element.id
                        }));
                        break;
                    default:
                        throw "unknown form type: " + element.type;
                }
            });
            // Turn the form into a dialog box
            form.dialog({
                modal: true,
                autoOpen: false,
                title: jsonData.title,
                draggable: true,
                closeOnEscape: false,
                open: function() {
                    $(".ui-dialog-titlebar-close").hide();
                    $.each(jsonData.elements, function(i_unused, element) {
                        $("input", "div#" + element.id).removeClass("ui-state-error");
                    });
                },
                buttons: {
                    "Ok": function() {
                        // validate form (require all field be filled in)
                        // if valid, process feature, then close
                        // alert("VALIDATE FORM HERE");

                        var valid = true;
                        $.each(jsonData.elements, function(i_unused, element) {
                            switch (element.type) {
                                case "text":
                                    if ($("input", "div#" + element.id).val() === "") {
                                        $("input", "div#" + element.id)
                                        .addClass("ui-state-error");
                                        valid = false;
                                    }
                                    break;
                            }
                        });
                        if (valid) {
                            MAIN.callbacks.processFeature(form.feature);
                            $(this).dialog("close");
                        }
                    },
                    "Cancel": function() {
                        // This gets triggered if the dialog closes with the X
                        // button, that means
                        // we haven't done a submit and we should just throw away
                        // the feature
                        if (MAIN.dialogs.shapeMetadataDialog.feature.layer === MAIN.variables.layers.userLayer) {
                            MAIN.variables.layers.userLayer.removeFeatures([ MAIN.dialogs.shapeMetadataDialog.feature ]);
                            $(this).dialog("close");
                        }
                    }
                },
                beforeClose: function() {
                }
            });
            return form;
        }());
    };
    /**
     * This callback is invoked by OpenLayers when a new geometry has been
     * digitized.
     */
    MAIN.callbacks.onFeatureInsert = function(feature) {
        MAIN.dialogs.shapeMetadataDialog.feature = feature;
        // the dialog has a callback on its buttons that process the feature if the
        // dialog is filled out correctly
        MAIN.dialogs.shapeMetadataDialog.dialog("open");
    };
    /**
     * This callback is invoked when a user has completed a feature metadata form
     * correctly. It processes the shape by placing it in the correct layer and
     * updates the global geojson object.
     */
    MAIN.callbacks.processFeature = function(feature) {

        // Read the values from the input form and attach to attributes of feature object
        $.each(MAIN.formjson.elements, function(i_unused, formElement) {
            var element = $("div#" + formElement.id);

            switch(formElement.type) {
                case "text":
                    feature.attributes[formElement.id] = $("input", element).val();
                    break;
                case "radio":
                    feature.attributes[formElement.id] = $('input:radio:checked', element).val();
                    break;
                default:
                    throw "unknown formElement type: " + formElement.type;
            }
        });
        // Process feature type to determine which layer it should live in
        if (feature.attributes['Feature-Type'] === 'Future') {
            MAIN.variables.layers.userLayer.removeFeatures([ feature ]);
            MAIN.variables.layers.futureLayer.addFeatures([ feature ]);
        } else if (feature.attributes['Feature-Type'] === 'Existing') {
            MAIN.variables.layers.userLayer.removeFeatures([ feature ]);
            MAIN.variables.layers.existingLayer.addFeatures([ feature ]);
        }

        // Turn on the "save" button since the map has changed.
        // This indicates to the user that they can now send information to the
        // database.
        $("input.save").button({
            disabled: false,
            label: "Save"
        });
    };

    $.extend({
        saveMapState: function(options) {
            var geojsonText, postdata, metadata = {}, geojsonLayers = {};
            $("#data").empty();

            $.each([ "LineString", "Point", "Polygon" ], function(i_unused, type) {
                geojsonLayers[type] = {
                    features: [],
                    name: type,
                    type: "FeatureCollection"
                };
            });
            $.each(MAIN.variables.userMetadata, function(key,value) {
                if (key.charAt(0) !== "_") {
                    metadata[key] = value;
                }
            });
            $.each([MAIN.variables.layers.existingLayer, MAIN.variables.layers.futureLayer], function(i_unused, layer) {
                $.each(layer.features, function(i_unused, feature) {
                    var lonLat, geojsonFeature = {};

                    geojsonFeature.properties = feature.attributes;
                    $.extend(geojsonFeature.properties,metadata);

                    switch(feature.geometry.CLASS_NAME) {
                        case "OpenLayers.Geometry.Point":
                            lonLat = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y).transform(MAIN.variables.map.getProjectionObject(), MAIN.variables.lngLatProj);
                            // directly define the point
                            geojsonFeature.geometry = {
                                type: "Point",
                                coordinates: [ lonLat.lon, lonLat.lat ]
                            };
                            geojsonLayers.Point.features.push(geojsonFeature);
                            break;
                        case "OpenLayers.Geometry.LineString":
                            geojsonFeature.geometry = {
                                type: "LineString",
                                coordinates: []
                            };
                            // Loop through each coordinate in the line
                            $.each(feature.geometry.components, function(i, component) {
                                lonLat = new OpenLayers.LonLat(component.x, component.y)
                                .transform(MAIN.variables.map.getProjectionObject(), MAIN.variables.lngLatProj);
                                geojsonFeature.geometry.coordinates[i] = [ lonLat.lon, lonLat.lat ];
                            });
                            geojsonLayers.LineString.features.push(geojsonFeature);
                            break;
                        case "OpenLayers.Geometry.Polygon":
                            geojsonFeature.geometry = {
                                type: "Polygon",
                                coordinates: []
                            };
                            $.each(feature.geometry.components, function(i, polyComponent) {
                                // The two loops handle cases where polygons might have holes.
                                geojsonFeature.geometry.coordinates[i] = [];
                                $.each(polyComponent.components, function(j, component) {
                                    lonLat = new OpenLayers.LonLat(component.x, component.y)
                                    .transform(MAIN.variables.map
                                    .getProjectionObject(), MAIN.variables.lngLatProj);
                                    geojsonFeature.geometry.coordinates[i][j] = [ lonLat.lon, lonLat.lat ];
                                });
                            });
                            geojsonLayers.Polygon.features.push(geojsonFeature);
                            break;
                        default:
                            throw "unknown feature class: " + feature.geometry.CLASS_NAME;
                    }
                });
            });
            if (options && options.postUrl) {
                geojsonText = JSON.stringify(geojsonLayers);

                postdata = {
                    user: MAIN.variables.userMetadata.Name,
                    metadata: metadata,
                    geojson: geojsonText,
                    uuid: MAIN.variables.userMetadata.uuid
                };
                $("input.save").button({
                    disabled: true,
                    label: "Saving"
                });
                $.ajax({
                    type: 'POST',
                    url: options.postUrl,
                    data: postdata,
                    dataType: "json",
                    success: function() {
                        $("input.save").button({
                            disabled: true,
                            label: "Saved"
                        });
                    },
                    error: function(jqXHR) {
                        $("#data").append("<p class='ui-state-error-text'>Error '" + jqXHR.status + " " + jqXHR.statusText + "' when attempting to send data.  Your map was not saved.  Please email us.</p>");
                        $("input.save").button({
                            disabled: false,
                            label: "Save Now"
                        });
                    }
                });
            }
        }
    });

    $.extend({
        displayUserStatus: ( function() {
            var userInfoDiv = $('<div>').css({
                'display': 'none',
                'background-color': '#fff',
                'border-width': 1,
                'border-color': '#555',
                'border-style': 'solid',
                'border-radius': 5,
                '-moz-border-radius': 15,
                'z-index': 1000,
                position: 'absolute'
            }).attr({
                id: "userInfo-popup"
            }).hide().appendTo('body');

            return function () {
                $("#userInfoMessage").remove();// delete message if it's there
                if (MAIN.variables.userMetadata.Name !== null) {

                    $("#welcome-message").empty();
                    $("#welcome-message").append('<span id="login-id">Logged in as: <strong>' + MAIN.variables.userMetadata.Name + '<strong\/> <input id="logout" class="small-button" type="button" value="Logout"></input></span>');
                    $("#login-id").hover( function handlerIn(e) {
                        var boxText = '';
                        //build up the user info to display
                        $.each(MAIN.variables.userMetadata, function(key,value) {
                            boxText += '<strong>'+key+'<\/strong> ';
                            boxText += value + '<\/br>';
                        });
                        userInfoDiv.html(boxText);
                        userInfoDiv.show();
                        userInfoDiv.show().css({
                            position: 'absolute',
                            top: e.pageY + 7,
                            left: e.pageX + 7- userInfoDiv.width()
                        });
                    }, function handlerOut(e) {
                        userInfoDiv.hide();
                    }
                    );
                    $("body").append(userInfoDiv);
                    $("#logout").button({
                        title: "Logout"
                    }).click( function() {
                        //First remove all the user input
                        $.each(MAIN.variables.layers, function(name, layer) {
                            //Skip over the first layer since it's not user defined
                            if (name === "userLayer") {
                                return true;
                            }
                            layer.removeAllFeatures();
                        });
                        //Now do the login process
                        $.login();
                    });
                }
            };
        }())
    });

    $.extend({
        /*
         * login: takes care of the login process. Displays a login dialog
         * and populates metadata object status with user, district, uuid
         */
        login: function() {
            $("input.save").button({
                disabled: true,
                label: "Map not yet modified"
            });
            MAIN.variables.userMetadata.uuid = $.generateUUID();
            MAIN.dialogs.loginDialog.dialog("open");
        }
    });

    $.extend({
        /**
         * initalizeMap: called to create a new openlayers map on a google
         * physical layers map. Returns an object describing the lat/lng
         * projection and the OpenLayers map
         */
        initalizeMap: function() {
            var bounds, layerArray, googlePhysicalLayer,
            googleSatelliteLayer, googleStreetsLayer, defaultStyleMap, existingStyleMap,
            futureStyleMap;

            // Initialize map, 'map_div' is the id of the div on the main
            // page.
            // it would be nice if this weren't hard coded
            MAIN.variables.map = new OpenLayers.Map("map_div", {
                // The EPSG:900913 is what google maps uses as a projection
                projection: new OpenLayers.Projection("EPSG:900913"),
		controls: [new OpenLayers.Control.Navigation(), new OpenLayers.Control.PanZoomBar(), new OpenLayers.Control.ScaleLine()]
            });
            MAIN.variables.map.addControl(new OpenLayers.Control.OverviewMap({
                layers: [new OpenLayers.Layer.OSM()],
                mapOptions: {projection: new OpenLayers.Projection("EPSG:4326")},
                autoPan:true,
                minRatio:16
            }));
	    googlePhysicalLayer = new OpenLayers.Layer.Google("Google Physical", {
                sphericalMercator: true,
                type: google.maps.MapTypeId.TERRAIN
            });
            googleSatelliteLayer = new OpenLayers.Layer.Google("Google Satellite", {
                sphericalMercator: true,
                type: google.maps.MapTypeId.SATELLITE,
		numZoomLevels: 22
            });
	    googleStreetsLayer = new OpenLayers.Layer.Google("Google Streets", {
                sphericalMercator: true,
		numZoomLevels: 20
            });
            var osm = new OpenLayers.Layer.OSM();

            // This is the WGS84 lat/lng pair coordinates in degrees with
            // Greenwish
            // as the central median useful for unprojecting coordinates
            // from map
            // projection for storage
            MAIN.variables.lngLatProj = new OpenLayers.Projection("EPSG:4326");

            // TODO: convert style maps to one single map with style rules
            // for
            // various feature types
            defaultStyleMap = new OpenLayers.StyleMap({
                'default': OpenLayers.Util.applyDefaults({
                    fillColor: '#aaa',
                    fillOpacity: 0.4,
                    strokeWidth: 1,
                    strokeColor: '#333',
                    pointRadius: 6
                })
            });

            existingStyleMap = new OpenLayers.StyleMap({
                'default': OpenLayers.Util.applyDefaults({
                    fillColor: '#0fa',
                    fillOpacity: 0.4,
                    strokeWidth: 1,
                    strokeColor: '#084',
                    pointRadius: 6
                }),
                'select': OpenLayers.Util.applyDefaults({
                    fillColor: '#0f3',
                    fillOpacity: 0.7,
                    strokeWidth: 3,
                    strokeColor: '#0f3',
                    pointRadius: 6
                })
            });

            futureStyleMap = new OpenLayers.StyleMap({
                'default': OpenLayers.Util.applyDefaults({
                    fillColor: '#f09',
                    fillOpacity: 0.4,
                    strokeWidth: 1,
                    strokeColor: '#a05',
                    pointRadius: 6
                }),
                'select': OpenLayers.Util.applyDefaults({
                    fillColor: '#f03',
                    fillOpacity: 0.7,
                    strokeWidth: 3,
                    strokeColor: '#f03',
                    pointRadius: 6
                })
            });

            MAIN.variables.layers.userLayer = new OpenLayers.Layer.Vector("User Overlay", {
                styleMap: defaultStyleMap
            });

            MAIN.variables.layers.existingLayer = new OpenLayers.Layer.Vector("Existing", {
                styleMap: existingStyleMap
            });

            MAIN.variables.layers.futureLayer = new OpenLayers.Layer.Vector("Future", {
                styleMap: futureStyleMap
            });

            //Load the user defined base layers and insert them in userLayer
            $.each(MAIN.userOverlaySources || [], function(i_unused, source) {
                $.ajax({
                    type: 'GET',
                    url: source,
                    dataType: "text",
                    async: false, // This is to get the callback to the userlayer set properly
                    success: function(data) {
                        var jsonParser = new OpenLayers.Format.GeoJSON();
                        var featureCollection = jsonParser.read(data);

                        // This loop centers the map on the current imported geometry
                        for (var i = 0; i < featureCollection.length; i += 1) {
                            if (!bounds) {
                                bounds = featureCollection[i].geometry.getBounds();
                            } else {
                                bounds.extend(featureCollection[i].geometry.getBounds());
                            }
                        }
                        MAIN.variables.layers.userLayer.addFeatures(featureCollection);

                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        console.error("Couldn't fetch "+ source);
                        console.log(jqXHR);
                        console.log(textStatus);
                        console.log(errorThrown);
                    }
                });
            });
            // Add a callback to popup dialogs on feature insert. We do this
            // after the above ajax so it doesn't trigger a feature insert every time
            // we add something. It's also why those requests are asynchronous
            MAIN.variables.layers.userLayer.onFeatureInsert = MAIN.callbacks.onFeatureInsert;

            layerArray = [];
            $.each(MAIN.variables.layers, function(i_unused, val) {
                layerArray.push(val);
            });
            MAIN.variables.map.addLayers(layerArray);
            MAIN.variables.map.addLayers([ googlePhysicalLayer, googleSatelliteLayer, googleStreetsLayer, osm ]);
            MAIN.variables.map.addControl(new OpenLayers.Control.LayerSwitcher());

            var editToolbar = new OpenLayers.Control.EditingToolbar(MAIN.variables.layers.userLayer);
            editToolbar.saveState = true;
            MAIN.variables.map.addControl(editToolbar);

            //Zoom the map on the region defined by the userlayer
            if (bounds) {
                MAIN.variables.map.zoomToExtent(bounds);
            } else {
                var proj = new OpenLayers.Projection("EPSG:4326");
                var point = new OpenLayers.LonLat(MAIN.variables.viewport.lng, MAIN.variables.viewport.lat);
                point.transform(proj, MAIN.variables.map.getProjectionObject());
                MAIN.variables.map.setCenter(point,MAIN.variables.viewport.zoom);
            }

            MAIN.variables.controls.hoverControls = new OpenLayers.Control.SelectFeature(layerArray, ( function() {
                // This is the element that will display the feature.geometry.properties over
                // the mouse pointer
                var div = $('<div>').css({
                    'display': 'none',
                    'background-color': '#fff',
                    'border-width': 1,
                    'border-color': '#555',
                    'border-style': 'solid',
                    'border-radius': 5,
                    '-moz-border-radius': 15,
                    'z-index': 1000
                }).attr({
                    id: "feature-popup"
                }).hide().appendTo('body'), // This function gets called as the mouse pointer hovers over a feature
                hoverFunction = MAIN.callbacks.hoverFunction(div);
                // This is the properties object to pass to the SelectFeature constructor
                return {
                    hover: true,
                    onSelect: function(feature) {
                        // on select, build an HTML table of the feature.geometry.properties
                        var text = "<table>";
                        $.each(feature.attributes, function(key, value) {
                            text = text + "<tr><th style='text-align: right'>" + key + "<\/th><td>" + value + "<\/td><\/tr>";
                        });
                        text = text + '<\/table>';
                        // bind mousemove to the function that changes the position of the div
                        $('body').mousemove(hoverFunction);
                        feature.hoverFunction = hoverFunction;
                        feature.popup = div;
                        //If we're on a layer that was defined by user input, allow the features to
                        //be deleted
                        if (feature.layer !== MAIN.variables.layers.userLayer) {
                            MAIN.variables.selectedFeature = feature;
                            text = text + '<table>';
                            text = text + '<tr><td>press <i class="ui-state-highlight">Insert</i> to edit feature<\/td><\/tr>';
                            text = text + '<tr><td>press <i class="ui-state-highlight">Delete</i> to remove feature<\/td><\/tr>';
                            text = text + '<\/table>';
                        }
                        div.html(text);
                    },
                    onUnselect: function() {
                        // on unselect, turn off the mouse move function and hide the hover element
                        $('body').unbind('mousemove', hoverFunction);
                        MAIN.variables.selectedFeature = null;
                        div.hide();
                    }
                };
            }()));
            //This is to handle cases where poygons abut the edge of the map and
            //the user mouses out.  Unfortunately mousing back in doesn't re-enable
            //the hover control.
            $("div#info").hover( function(e) {
                MAIN.variables.controls.hoverControls.unselectAll();
            });
            // after constructing hover controls, add them to the map and activate
            MAIN.variables.map.addControl(MAIN.variables.controls.hoverControls);
            MAIN.variables.controls.hoverControls.activate();

            //Keyboard handler
            var keyboardControl = new OpenLayers.Control();
            OpenLayers.Util.extend(keyboardControl, (function () {
                
                var div = $('<div>').css({
                    'display': 'none',
                    'background-color': '#fff',
                    'border-width': 1,
                    'border-color': '#555',
                    'border-style': 'solid',
                    'border-radius': 5,
                    '-moz-border-radius': 15,
                    'z-index': 1000
                }).attr({
                    id: "feature-popup"
                }).hide().appendTo('body');
                var hoverFunction = MAIN.callbacks.hoverFunction(div);
                
                $.each(MAIN.variables.layers, function(unused_i, layer) {
                    //This loop sets the clickout controls for turning off the modify
                    //feature.  When the user clicks out, it triggers an 'afterfeaturemodified'
                    //event.
                    layer.events.register("afterfeaturemodified",this,function(e) {
                        MAIN.variables.controls.modifyControl.deactivate();
                        MAIN.variables.controls.hoverControls.activate();
                        $('body').unbind('mousemove', hoverFunction);
                        div.hide();
                    });
                    
                });
                
                //This loop adds a hook to the menu bar buttons that shuts down
                //the modifycontrol tool if a new tool is selected
                $.each(editToolbar.controls, function(unused_i,control) {
                    control.events.register("activate",this, function(e){
                        if (MAIN.variables.controls.modifyControl) {
                            MAIN.variables.controls.modifyControl.deactivate();
                        }
                    });
                });
                
                //This section continuously tracks the mouse position so that
                //whenever the app needs it, it can be indexed out of a variable
                var currentMousePos = null;
                $(function() {
                    $(document).mousemove(function(e){
                        if (e.pageX && e.pageY) {
                            currentMousePos = {
                                x: e.pageX,
                                y: e.pageY
                            }
                        }
                    });
                });
                
                return {
                    draw: function() {
                        this.handler = new OpenLayers.Handler.Keyboard( this, {
                            "keydown": function(evt) {
                                switch(evt.keyCode) {
                                    case 46: //del key; see unixpapa.com/js/key.html
                                        var feature = MAIN.variables.selectedFeature;
                                        if (feature && feature.layer && feature.layer !== MAIN.variables.layers.userLayer) {
                                            feature.layer.removeFeatures([feature]);
                                            $('body').unbind('mousemove',feature.hoverFunction);
                                            feature.popup.hide();
                                            $("input.save").button({
                                                disabled: false,
                                                label: "Save"
                                            });
                                        }
                                        break;
                                    case 45: //insert key; see  unixpapa.com/js/key.html
                                        var feature = MAIN.variables.selectedFeature;
                                        if (feature && feature.layer && feature.layer !== MAIN.variables.layers.userLayer) {
                                            //For editing features on 'insert' keypress
                                            //If the control exists from a previous modify, delete it and create a new one
                                            //We do this because the control needs to exist for the layer, but we may have many
                                            //layers and it doens't make sense to have a separate control for each layer
                                            if (MAIN.variables.controls.modifyControl) {
                                                MAIN.variables.map.removeControl(MAIN.variables.controls.modifyControl);
                                                MAIN.variables.controls.modifyControl.destroy();
                                            }
                                            MAIN.variables.controls.modifyControl = new OpenLayers.Control.ModifyFeature(feature.layer, {
                                                mode: OpenLayers.Control.ModifyFeature.DRAG | OpenLayers.Control.ModifyFeature.RESHAPE
                                            });
                                            MAIN.variables.map.addControl(MAIN.variables.controls.modifyControl);
                                            
                                            //Figure out which control is currently selected on the toolbar
                                            $.each(editToolbar.controls, function(i_unused,control){
                                                if (control.active) {
                                                    control.deactivate();
                                                }
                                            });
                                            editToolbar.redraw();
                                            MAIN.variables.controls.hoverControls.unselectAll();
                                            MAIN.variables.controls.hoverControls.deactivate();
                                            MAIN.variables.controls.modifyControl.activate();
                                            MAIN.variables.controls.modifyControl.selectControl.clickFeature(feature);
                                            
                                            feature.hoverFunction = hoverFunction;
                                            feature.popup = div;
                                            
                                            //Create helpful div to show edit mode
                                            var text = "<p><strong>Edit Mode</strong> Click outside the shape to finish editing.<\/p>";
                                            div.html(text);
                                            $('body').mousemove(hoverFunction).mousemove();
                                            div.css({
                                                top: currentMousePos.y - div.height() - 7,
                                                left: currentMousePos.x + 7
                                            });
                                        }
                                        break;
                                }
                            }
                        });
                    }
                }
            }()));
            MAIN.variables.map.addControl(keyboardControl);
            keyboardControl.activate();
        }
    });

    // This fetches a JSON object to define user level metadata (the login dialog)
    $( function loadUserMetadata() {
        $.ajax({
            type: 'GET',
            url: 'data/user_metadata.json',
            dataType: "json",
            success: function(data) {
                MAIN.callbacks.buildLoginDialog(data);
                $.login();
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.error("Couldn't fetch data/user_metadata.json");
                console.log(jqXHR);
                console.log(textStatus);
                console.log(errorThrown);
            }
        });
    });
    /** Invoked when the DOM is completely loaded */
    $( function initializeButtons() {
        // turn all the buttons into jQuery buttons
        $("input:button").button();
        $("input.reset").click( function() {
            if (confirm("Are you sure you want to erase your map and start over?")) {
                $.each(MAIN.variables.layers, function(name, layer) {
                    //Skip over the first layer since it's not user defined
                    if (name === "userLayer") {
                        return true;
                    }
                    layer.removeAllFeatures();
                });
                MAIN.variables.userMetadata.uuid = $.generateUUID();
                $("#data").empty();
                $("#userInfoMessage").remove();
            }
        });
        // Saves current map annotation data to a database on click.
        $("input.save").button({
            disabled: true,
            label: "Map not yet modified"
        }).click( function() {
            //If modify control exists, disable it to turn off edit mode
            //Otherwise the vertex handles will get saved along with the 
            //geometry.
            if (MAIN.variables.controls.modifyControl) {
                MAIN.variables.controls.modifyControl.deactivate();
            }
            $.saveMapState({
                postUrl: MAIN.variables.cgi_path+'/send_data.php'
            });
        });
    });
    $( function main() {
        //Turn info screen into collapsable element
        $("#instructions").accordion({
            collapsible: true,
            change: MAIN.callbacks.resizeFunction,
            autoHeight: false
        });

        //when the window is resized, call this function,
        //also get it queued up to call as soon as possible
        $(window).resize(MAIN.callbacks.resizeFunction).resize();

        // This fetches a JSON object to point to the predefined user overlays
        $.ajax({
            type: 'GET',
            url: 'data/user_overlays.json',
            dataType: "json",
            async: false, // This is to get the callback to the userlayer set properly
            success: function(data) {
                MAIN.userOverlaySources = data.layers;
                MAIN.variables.viewport = {
                    zoom: data.zoom,
                    lat: data.lat,
                    lng: data.lng
                };
            },
            error: function(jqXHR, textStatus, errorThrown) {
                console.error("Couldn't fetch data/user_overlays.json");
                console.log(jqXHR);
                console.log(textStatus);
                console.log(errorThrown);
            }
        });

        //Build up the map data structure
        $.initalizeMap();

        // Triggered when user navigates away from the page, placeholder
        // for adding something in the future
        $(window).unload( function() {
            console.log('goodbye');
        });
        //Don't let the user leave without letting them know they didn't save
        window.onbeforeunload = function() {
            if (!$("input.save").button("option", "disabled")) {
                return "You haven't saved your map, are you sure you want to leave?";
            }
        };
    });
});